package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"time"

	"strconv"
	"strings"

	"github.com/go-redis/redis"
)

type KeyMap interface { //interface
	Get(string) (string, error) //key to id
	Close() error               //
}

type RepeaterKeyMap struct {
	mod int
}

func (r *RepeaterKeyMap) Get(x string) (string, error) {
	s := strings.Split(x, ".") //通过.分割
	if len(s) != 4 {
		return "", fmt.Errorf("err format")
	}
	id, err := strconv.Atoi(s[3])
	if err != nil {
		return "", err
	}
	id = id % r.mod //模13
	return fmt.Sprintf("%d", id), err
}

func (r *RepeaterKeyMap) Close() error {
	return nil
}

type RedisKeyMap struct {
	Timeout       int      `json:"time_out"`
	SentinelAddrs []string `json:"sentinel_addrs"`
	Name          string   `json:"name"`
	Password      string   `json:"passwd"`
	Db            int      `json:"db"`
	MaxRetries    int      `json:"max_retry"`
	redisCli      *redis.Client
}

func NewRedisKeyMap(fn string) (*RedisKeyMap, error) {
	b, err := ioutil.ReadFile(fn)
	if err != nil {
		return nil, err
	}
	var ret *RedisKeyMap
	err = json.Unmarshal(b, &ret) //放在ret里面了
	if err != nil {
		return nil, err
	}
	ret.redisCli = redis.NewFailoverClient(&redis.FailoverOptions{
		MasterName:    ret.Name,
		SentinelAddrs: ret.SentinelAddrs,
		DB:            ret.Db,
		Password:      ret.Password,
	})
	v, err := ret.redisCli.Ping().Result() //看有没有连上
	LOG.Infof("Redis Ping--o---o---o--%s\n", v)

	if err != nil {
		return nil, err
	}
	return ret, nil
}

func (km *RedisKeyMap) Close() error {
	return km.redisCli.Close()
}

func (km *RedisKeyMap) Get(key string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(km.Timeout))
	ch := make(chan struct {
		string
		error
	}) //就是有加上error的方法

	go func() {
		v, err := km.redisCli.Get(key).Result() //得到v的值
		ch <- struct {
			string
			error
		}{v, err} //通道
	}()

	defer cancel()

	select { //如果select 选项都阻塞了，则select一直等待到有一个收到为之
	case v := <-ch: //找到值了，返回v
		return v.string, v.error
	case <-ctx.Done(): //Done方法返回一个channel,是表达Context是否已关闭的信号
		return "", fmt.Errorf("redis time out")
	}
}
